package de.zainodis.triplog.ui;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.io.File;
import java.util.Date;

import de.zainodis.triplog.R;
import de.zainodis.triplog.data.DataProvider;
import de.zainodis.triplog.data.TripEntry;
import de.zainodis.triplog.ui.fragment.GalleryPreviewFragment;
import de.zainodis.triplog.ui.fragment.NoteFlipperFragment;
import de.zainodis.triplog.ui.fragment.WeatherPreviewFragment;
import de.zainodis.triplog.ui.task.CurrentTripLoader;
import de.zainodis.triplog.ui.task.IAsyncTaskParent;
import de.zainodis.triplog.ui.task.SaveNoteService;
import de.zainodis.triplog.util.DateTimeUtils;
import de.zainodis.triplog.util.Utilities;

/**
 * Main screen of the application.
 */
public class DashboardActivity extends Activity implements IAsyncTaskParent<TripEntry>, IAlertDialogHost {

    //TODO display target destination somewhere
    private static final String TAG = "DashboardActivity";
    private static final String TAG_DIALOG = "Dialog";

    //Use this to pass a trip via the intent bundle.
    public static final String BUNDLE_IMAGE_URI = "ImageUri";
    public static final String BUNDLE_CURRENT_LOCATION = "CurrentLocation";
    /**
     * Only used when showing already completed trips, passed via intent.
     */
    public static final String BUNDLE_TRIP_ID = "TripId";


    private static final int TAKE_PICTURE_REQUEST_CODE = 1;

    private Uri imageUri;
    private LocationListener locationListener = null;
    private Location currentLocation = null;
    private BroadcastReceiver broadcastReceiver;

    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    private ListView mDrawerList;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dashboard);
        //Required so the navigation drawer indicator shows up
        getActionBar().setHomeButtonEnabled(true);
        getActionBar().setDisplayShowHomeEnabled(true);
        getActionBar().setDisplayHomeAsUpEnabled(true);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.navigation_drawer);

        // Set the adapter for the list view
        mDrawerList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.navigation_drawer)));
        // Set the list's click listener
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());


        if (savedInstanceState != null) {
            imageUri = savedInstanceState.getParcelable(BUNDLE_IMAGE_URI);
            currentLocation = savedInstanceState.getParcelable(BUNDLE_CURRENT_LOCATION);
        }

        if (DataProvider.getCurrentTrip() == null) {
            //Launch loader for loading a trip
            new CurrentTripLoader(this, this).execute(false);
        } else {
            loadDashboard();
        }

        mDrawerToggle = new ActionBarDrawerToggle(
                this,
                mDrawerLayout,
                R.drawable.ic_drawer,
                R.string.drawer_open,
                R.string.drawer_close) {

            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                // creates call to onPrepareOptionsMenu()
                getActionBar().setTitle(getString(R.string.dashboard));
                invalidateOptionsMenu();
            }

            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                // creates call to onPrepareOptionsMenu()
                getActionBar().setTitle(getString(R.string.app_name));
                invalidateOptionsMenu();
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        //Register a local broadcast receiver
        if (broadcastReceiver == null) {
            broadcastReceiver = new UpdateReceiver();
        }
        IntentFilter filter = new IntentFilter();
        filter.addAction(SaveNoteService.ACTION_SAVE_NOTE_SERVICE);
        LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, filter);

    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }


    @Override
    public void onOk() {
        //Start the trip wizard, the current trip wont be cancelled until the user actually created a new trip
        startActivity(new Intent(this, TripWizardLocation.class));
    }


    private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
            switch (position) {
                case 0:
                    Log.i(TAG, "Selected new trip.");
                    NewTripWarning newFragment = NewTripWarning.newInstance(0);
                    newFragment.show(getFragmentManager(), TAG_DIALOG);
                    break;
                case 1:
                    Log.i(TAG, "Selected review old trips.");
                    startActivity(new Intent(DashboardActivity.this, TripEntryListActivity.class));
                    break;
                case 2:
                    Log.i(TAG, "Selected Settings.");
                    startActivity(new Intent(DashboardActivity.this, SettingsActivity.class));
                    break;
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Subscribe to location updates
        LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        locationListener = new LocationListenerImpl();
        // Register the listener with the Location Manager to receive location updates
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

    }

    @Override
    protected void onPause() {
        super.onPause();
        try {
            LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
            locationManager.removeUpdates(locationListener);
        } catch (Throwable e) {
            Log.w(TAG, "Error while trying to un-subscribe from location updates.", e);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        try {
            LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
            broadcastReceiver = null;
        } catch (Throwable e) {
            Log.w(TAG, "Failed to unregister this broadcast receiver.", e);
        }
    }

    /**
     * Make sure we have an ongoing trip before calling this method.
     */
    private void loadDashboard() {
        //Reload the options menu
        invalidateOptionsMenu();
        updateGallery();
        updateWeather();
        updateNotes();
    }

    private void updateGallery() {
        GalleryPreviewFragment galleryFragment = (GalleryPreviewFragment) getFragmentManager().findFragmentById(R.id.dashboard_gallery);
        galleryFragment.onUpdate(this);
    }

    private void updateWeather() {
        WeatherPreviewFragment weatherFragment = (WeatherPreviewFragment) getFragmentManager().findFragmentById(R.id.dashboard_weather);
        weatherFragment.onUpdate(this);
    }

    private void updateNotes() {
        NoteFlipperFragment notesFragment = (NoteFlipperFragment) getFragmentManager().findFragmentById(R.id.dashboard_diary);
        notesFragment.onUpdate(this);
    }


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (imageUri != null) {
            outState.putParcelable(BUNDLE_IMAGE_URI, imageUri);
        }

        if (currentLocation != null) {
            outState.putParcelable(BUNDLE_CURRENT_LOCATION, currentLocation);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (DataProvider.hasOngoingTrip()) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.dashboard, menu);
            return true;
        } else {
            //Don't show the option menu if the trip has been completed already
            return false;
        }
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        //Called whenever we call invalidateOptionsMenu()
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        // If the nav drawer is open, hide action items related to the content view
        menu.findItem(R.id.action_new_note).setVisible(!drawerOpen);
        menu.findItem(R.id.action_map).setVisible(!drawerOpen);
        menu.findItem(R.id.action_camera).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        int id = item.getItemId();
        if (id == R.id.action_camera) {
            //Take a picture
            onTakePicture();
            return true;
        } else if (id == R.id.action_new_note) {
            startActivity(new Intent(this, EditNoteActivity.class));
            return true;
        } else if (id == R.id.action_map) {
            //If we got a current location take that, otherwise use the location of the trip
            String mapRequestUri = "http://maps.google.co.in/maps?q=" + DataProvider.getCurrentTrip().getLocation().getLocationSetting();
            if (currentLocation != null) {
                mapRequestUri = String.format("http://maps.google.com/maps?q=loc:%s,%s (%s)", currentLocation.getLatitude(), currentLocation.getLongitude(), getString(R.string.current_location));
            }
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mapRequestUri));
            //So we return to our app if back is pressed
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        }
        return super.onOptionsItemSelected(item);
    }


    private void onTakePicture() {
        Intent imageIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        String timeStamp = DateTimeUtils.getFormattedDate(new Date(), DateTimeUtils.TIMESTAMP);

        File photoDirectory = Utilities.getPhotoDirectory();
        //Create a new file for the photo
        File image = new File(photoDirectory, String.format("IMG_%s.jpg", timeStamp));
        imageUri = Uri.fromFile(image);

        imageIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(imageIntent, TAKE_PICTURE_REQUEST_CODE);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case TAKE_PICTURE_REQUEST_CODE:
                if (resultCode == Activity.RESULT_OK) {
                    Uri selectedImage = imageUri;
                    Log.i(TAG, "A new picture has been recorded, starting media scan...");
                    //Trigger scan so we get updated with the picture we just took
                    MediaScannerConnection.scanFile(this, new String[]{selectedImage.getPath()}, new String[]{"image/jpeg"}, new MediaScannerConnection.OnScanCompletedListener() {

                        @Override
                        public void onScanCompleted(String path, Uri uri) {
                            Log.i(TAG, "Rescan of gallery completed, updating gallery view.");
                            if (!isFinishing()) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        updateGallery();
                                    }
                                });
                            }
                        }
                    });
                }
        }
    }


    @Override
    public void onPostExecute(TripEntry result) {
        DataProvider.setCurrentTrip(result);

        if (DataProvider.getCurrentTrip() == null) {
            //Start wizard for creating a new trip
            startActivity(new Intent(this, TripWizardLocation.class));
        } else {
            //Load the UI
            loadDashboard();
        }
    }

    private class UpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (SaveNoteService.ACTION_SAVE_NOTE_SERVICE.equals(action)) {
                //Update displayed notes
                updateNotes();
            }
        }
    }


    private class LocationListenerImpl implements LocationListener {


        public void onLocationChanged(Location location) {
            // Called when a new location is found by the network location provider.
            currentLocation = location;
        }

        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

        public void onProviderEnabled(String provider) {
        }

        public void onProviderDisabled(String provider) {
        }

    }

}
